#ifndef _DRIVER_SPI_H_
#define _DRIVER_SPI_H_

#include <stdint.h>
#include <stdbool.h>

#define SPIM0                   SSIM0_BASE
#define SPIM1                   SSIM1_BASE
#define SPIS0                   SSIS_BASE
#define SPI_FIFO_SIZE           32

#define SPI_INT_TXEIS           0x01
#define SPI_INT_TXOIS           0x02
#define SPI_INT_RXUIS           0x04
#define SPI_INT_RXOIS           0x08
#define SPI_INT_RXFIS           0x10
#define SPI_INT_MSTIS           0x20

#define SPI_INT_TXEIM           0x01
#define SPI_INT_TXOIM           0x02
#define SPI_INT_RXUIM           0x04
#define SPI_INT_RXOIM           0x08
#define SPI_INT_RXFIM           0x10
#define SPI_INT_MSTIM           0x20

#define REG_WR(addr,value)      (*(volatile uint32_t *)(addr)) = (value);

enum spi_frame_format_t {
    SPI_FRAME_FORMAT_MOTOROLA,
    SPI_FRAME_FORMAT_TI,
    SPI_FRAME_FORMAT_MICROWIRE,
};

enum spi_frame_size_t {
    SPI_FRAME_SIZE_RSV0,
    SPI_FRAME_SIZE_RSV1,
    SPI_FRAME_SIZE_RSV2,
    SPI_FRAME_SIZE_4BITS,
    SPI_FRAME_SIZE_5BITS,
    SPI_FRAME_SIZE_6BITS,
    SPI_FRAME_SIZE_7BITS,    
    SPI_FRAME_SIZE_8BITS,
    SPI_FRAME_SIZE_9BITS,    
    SPI_FRAME_SIZE_10BITS,     
    SPI_FRAME_SIZE_11BITS,  
    SPI_FRAME_SIZE_12BITS,  
    SPI_FRAME_SIZE_13BITS,  
    SPI_FRAME_SIZE_14BITS,  
    SPI_FRAME_SIZE_15BITS,  
    SPI_FRAME_SIZE_16BITS,  
    SPI_FRAME_SIZE_17BITS,  
    SPI_FRAME_SIZE_18BITS,  
    SPI_FRAME_SIZE_19BITS,  
    SPI_FRAME_SIZE_20BITS,  
    SPI_FRAME_SIZE_21BITS,  
    SPI_FRAME_SIZE_22BITS,  
    SPI_FRAME_SIZE_23BITS,  
    SPI_FRAME_SIZE_24BITS,  
    SPI_FRAME_SIZE_25BITS,  
    SPI_FRAME_SIZE_26BITS,
    SPI_FRAME_SIZE_27BITS,
    SPI_FRAME_SIZE_28BITS,
    SPI_FRAME_SIZE_29BITS,
    SPI_FRAME_SIZE_30BITS,
    SPI_FRAME_SIZE_31BITS,
    SPI_FRAME_SIZE_32BITS,
};

enum spi_instruction_length_t {
    SPI_INSTRUCTION_LENGTH_0,
    SPI_INSTRUCTION_LENGTH_4,
    SPI_INSTRUCTION_LENGTH_8,
    SPI_INSTRUCTION_LENGTH_16,
};

enum spi_address_length_t {
    SPI_ADDRESS_LENGTH_0 = 0,
    SPI_ADDRESS_LENGTH_8 = 2,
    SPI_ADDRESS_LENGTH_16 = 4,
    SPI_ADDRESS_LENGTH_24 = 6,
    SPI_ADDRESS_LENGTH_32 = 8,
};

enum spi_transfer_mode_t {
    SPI_TRANSFER_MODE_TX_AND_RX,
    SPI_TRANSFER_MODE_TX_ONLY,
    SPI_TRANSFER_MODE_RX_ONLY,
    SPI_TRANSFER_MODE_EEPROM_READ,
};

enum spi_trans_type_t {
    SPI_TRANS_TYPE_INST_STAND_ADDR_STAND,   // Instruction and Address will be sent in Standard SPI Mode.
    SPI_TRANS_TYPE_INST_STAND_ADDR_CFG,     // Instruction will be sent in Standard SPI Mode and Address will be sent in the mode specified by SPI_FRF
    SPI_TRANS_TYPE_INST_CFG_ADDR_CFG,       // Both Instruction and Address will be sent in the mode specified by SPI_FRF
};

enum spi_data_frame_format_t {
    SPI_DATA_FRAME_FORMAT_STD,
    SPI_DATA_FRAME_FORMAT_DUAL,
    SPI_DATA_FRAME_FORMAT_QUAD,
};

enum spi_source_clk_t {
    SPI_SOURCE_CLK_48MHZ,
    SPI_SOURCE_CLK_96MHZ,
};

struct spi_ctr0_t {
    uint32_t dfs:4;         // Selects the data frame length
    uint32_t frf:2;         // Selects which serial protocol transfers the data
    uint32_t scph:1;        // Serial Clock Phase. Valid when the frame format (FRF) is set to Motorola SPI.
    uint32_t scpol:1;       // Serial Clock Polarity. Valid when the frame format (FRF) is set to Motorola SPI.
    uint32_t tmod:2;        // Transfer Mode. Selects the mode of transfer for serial communication
    uint32_t slv_oe:1;      // Slave Output Enable. Relevant only when the working as a serial-slave device
    uint32_t reserved1:1;
    uint32_t cfs:4;         // Selects the length of the control word for the Microwire frame format
    uint32_t reserved2:5;
    uint32_t spi_frf:2;     // Selects data frame format for Transmitting/Receiving the data
    uint32_t reserved3:1;
    uint32_t sste:1;
    uint32_t reserved4:7;
};

struct spi_ctr1_t {
    uint32_t ndf:16;        // When TMOD = 10 or TMOD = 11 , this register field sets the number of data frames to be continuously received by the DW_apb_ssi.
    uint32_t reserved:16;
};

struct spi_ssienr_t {
    uint32_t ssi_en:1;      // Enables and disables all DW_apb_ssi operations
    uint32_t reserved:31;
};

struct spi_mwcr_t {
    uint32_t mwmod:1;       // Defines whether the Microwire transfer is sequential or non-sequential
    uint32_t mdd:1;         // Defines the direction of the data word when the Microwire serial protocol is used
    uint32_t mhs:1;         // Used to enable and disable the busy/ready handshaking interface for the Microwire protocol when the DW_apb_ssi is configured as a serial-master device
    uint32_t reserved:29;
}; 

struct spi_ser_t {
   uint32_t SER;            // Each bit in this register corresponds to a slave select line (ss_x_n) from the DW_apb_ssi master.
};

struct spi_baudr_t {
    uint32_t sckdiv:16;     // The frequency of the sclk_out is derived from the following equation: Fsclk_out = Fssi_clk/SCKDV
    uint32_t reserved:16;
};

struct spi_txftlr_t {
    uint32_t tft;           // Controls the level of entries (or below) at which the transmit FIFO controller triggers an interrupt
};

struct spi_rxftlr_t {
    uint32_t rft;           // Controls the level of entries (or above) at which the receive FIFO controller triggers an interrupt
};

struct spi_txflr_t {
    uint32_t txtfl;         // Contains the number of valid data entries in the transmit FIFO.
};

struct spi_rxflr_t {
    uint32_t rxtfl;         // Contains the number of valid data entries in the receive FIFO.
};

struct spi_sr_t {
    uint32_t busy:1;        // When set, indicates that a serial transfer is in progress; when cleared indicates that the DW_apb_ssi is idle or disabled
    uint32_t tfnf:1;        // Set when the transmit FIFO contains one or more empty locations, and is cleared when the FIFO is full
    uint32_t tfe:1;         // When the transmit FIFO is completely empty, this bit is set
    uint32_t rfne:1;        // Set when the receive FIFO contains one or more entries and is cleared when the receive FIFO is empty
    uint32_t rff:1;         // When the receive FIFO is completely full, this bit is set.
    uint32_t txe:1;         // Set if the transmit FIFO is empty when a transfer is started
    uint32_t dcol:1;        // This informs the processor that the last data transfer was halted before completion. This bit is cleared when read.
    uint32_t reserved:25;
};

struct spi_imr_t {
    uint32_t txeim:1;       // Transmit FIFO Empty Interrupt Mask
    uint32_t txoim:1;       // Transmit FIFO Overflow Interrupt Mask
    uint32_t rxuim:1;       // Receive FIFO Underflow Interrupt Mask
    uint32_t rxoim:1;       // Receive FIFO Overflow Interrupt Mask
    uint32_t rxfim:1;       // Receive FIFO Full Interrupt Mask
    uint32_t mstim:1;       // Multi-Master Contention Interrupt Mask
    uint32_t reserved:26;
};

struct spi_isr_t {
    uint32_t txeis:1;       // Transmit FIFO Empty Interrupt Status
    uint32_t txois:1;       // Transmit FIFO Overflow Interrupt Status
    uint32_t rxuis:1;       // Receive FIFO Underflow Interrupt Status
    uint32_t rxois:1;       // Receive FIFO Overflow Interrupt Status
    uint32_t rxfis:1;       // Receive FIFO Full Interrupt Status
    uint32_t mstis:1;       // Multi-Master Contention Interrupt Status
    uint32_t reserved:26;
};

struct spi_risr_t {
    uint32_t txeis:1;       // Transmit FIFO Empty Interrupt Raw Status
    uint32_t txois:1;       // Transmit FIFO Overflow Interrupt Raw Status
    uint32_t rxuis:1;       // Receive FIFO Underflow Interrupt Raw Status
    uint32_t rxois:1;       // Receive FIFO Overflow Interrupt Raw Status
    uint32_t rxfis:1;       // Receive FIFO Full Interrupt Raw Status
    uint32_t mstis:1;       // Multi-Master Contention Interrupt Raw Status
    uint32_t reserved:26;
};

struct spi_txoicr_t {
    uint32_t txoicr:1;      // Read this bit to Clear Transmit FIFO Overflow Interrupt.
    uint32_t reserved:31;
};

struct spi_rxoicr_t {
    uint32_t rxoicr:1;      // Read this bit to Clear Receive FIFO Overflow Interrupt
    uint32_t reserved:31;
};

struct spi_rxuicr_t {
    uint32_t rxuicr:1;      // Read this bit to Clear Receive FIFO Underflow Interrupt.
    uint32_t reserved:31;
};

struct spi_msticr_t {
    uint32_t msticr:1;      // Read this bit to Clear Multi-Master Contention Interrupt.
    uint32_t reserved:31;
};

struct spi_icr_t {
    uint32_t icr:1;         // This register is set if any of the interrupts below are active. A read clears the ssi_txo_intr, ssi_rxu_intr, ssi_rxo_intr, and the ssi_mst_intr interrupts
    uint32_t reserved:31;
};

struct spi_dmacr_t {
    uint32_t rdmae:1;       // This bit enables/disables the receive FIFO DMA channel
    uint32_t tdmae:1;       // This bit enables/disables the transmit FIFO DMA channel
    uint32_t reserved:30;
};

struct spi_dmatdlr_t {
    uint32_t dmatdl;        // This bit field controls the level at which a DMA request is made by the transmit logic.
};

struct spi_dmardlr_t {
    uint32_t dmardl;        // This bit field controls the level at which a DMA request is made by the receive logic
};

struct spi_idr_t {
    uint32_t idr;           // The register contains the peripheral's identification code
};

struct spi_rx_sample_dly_t {
    uint32_t rsd:8;         // This register is used to delay the sample of the rxd input port
    uint32_t reserved:24;
};

struct spi_spi_ctrlr0_t {
    uint32_t trans_type:2;  // Selects whether DW_apb_ssi will transmit instruction/address either in Standard SPI mode or the SPI mode selected in CTRLR0
    uint32_t addr_l:4;      // This bit defines Length of Address to be transmitted
    uint32_t reserved0:2;
    uint32_t inst_l:2;      // Dual/Quad/Octal mode instruction length in bits
    uint32_t reserved1:1;
    uint32_t wait_cycles:5; // Number of wait cycles in Dual/Quad/Octal mode between control frames transmit and data reception
    uint32_t reserved2:16;
};

struct spi_reg_t  {
    struct spi_ctr0_t spi_ctr0;
    struct spi_ctr1_t spi_ctr1;
    struct spi_ssienr_t spi_ssienr;
    struct spi_mwcr_t spi_mwcr;
    
    struct spi_ser_t spi_ser;
    struct spi_baudr_t spi_baudr;
    struct spi_txftlr_t spi_txftlr;
    struct spi_rxftlr_t spi_rxftlr;

    struct spi_txflr_t spi_txflr;
    struct spi_rxflr_t spi_rxflr;
    struct spi_sr_t spi_sr;
    uint32_t spi_imr;

    uint32_t spi_isr;
    struct spi_risr_t spi_risr;
    struct spi_txoicr_t spi_txoicr;
    struct spi_rxoicr_t spi_rxoicr;
    
    struct spi_rxuicr_t spi_rxuicr;
    struct spi_msticr_t spi_msticr;
    struct spi_icr_t spi_icr;
    struct spi_dmacr_t spi_dmacr;
    
    struct spi_dmatdlr_t spi_dmatdlr;
    struct spi_dmardlr_t spi_dmardlr;
    struct spi_idr_t spi_idr;
    uint32_t spi_version_id;
    
    uint32_t spi_dr[36];
    struct spi_rx_sample_dly_t spi_rx_sample_dly;
    struct spi_spi_ctrlr0_t spi_spi_ctrlr0;
};

struct spi_config_t {
    uint32_t spi_address;
    enum spi_source_clk_t source_spi_clk;
    enum spi_frame_format_t spi_frame_format;
    enum spi_frame_size_t spi_frame_size;
    enum spi_data_frame_format_t data_format;
    enum spi_transfer_mode_t spi_transfer_mode;
    uint8_t spi_spch;
    uint8_t spi_scpol;
    bool slave_select_toggle;
    enum spi_trans_type_t trans_type;
    enum spi_instruction_length_t spi_instL;
    enum spi_address_length_t spi_addrL;
    
    uint32_t baud_rate;
};

void spi_init(struct spi_config_t *config);  
void spi_enable(uint32_t spi_addr); 
void spi_disable(uint32_t spi_addr);
void spi_wait_busy_bit(uint32_t spi_addr);
bool spi_fifo_is_busy(uint32_t spi_addr);
bool spi_rx_fifo_is_full(uint32_t spi_addr);
void spi_clean_fifo(uint32_t spi_addr);
void spi_send_byte(uint32_t spi_addr,const uint32_t c);
void spi_send_data(uint32_t spi_addr,uint8_t* data,uint32_t len);
void spi_send_data_16bits(uint32_t spi_addr, uint16_t* data, uint32_t len);
uint16_t spi_get_data(uint32_t spi_addr,uint8_t* data,uint16_t len);
void spi_qual_send(uint32_t spi_addr,uint16_t opcode,uint32_t addr,uint8_t *data,uint16_t len);
void test_spi(void);
void spi_set_int(uint32_t spi_addr,uint8_t int_type);
uint8_t spi_clear_isr(uint32_t spi_addr);
void spi_set_fifo_level(uint32_t spi_addr,uint8_t tft,uint8_t rft);
bool spi_tx_fifo_is_not_full(uint32_t spi_addr);
uint32_t spi_send_and_recv(uint32_t spi_addr,uint32_t c );
bool spi_tx_fifo_is_empty(uint32_t spi_addr);
uint32_t spi_get_isr_status(uint32_t spi_addr);
bool spi_rx_fifo_is_not_empty(uint32_t spi_addr);
void spi_multiline_read(uint32_t spi_addr,uint8_t opcode,uint32_t offset,uint8_t* data ,uint8_t length);
#endif

